#include "CompresorDelta.h"
#include "CompresorGamma.h"
#include <math.h>
#include <iostream>
#include "string.h"

CompresorDelta::CompresorDelta() {}

// Comprime un numero a código delta, devuelve la longitud en bits de dicho código
int CompresorDelta::comprimir(unsigned int x, unsigned char** delta) {
	if(x == 0)
		return -1;
	unsigned int long_gamma = 0;
	unsigned int num_gamma = (log10(x) / log10(2)) + 1;
	unsigned char *gamma = NULL;
	unsigned int cant_bytes_gamma = 0;
	unsigned int long_cod_binario = num_gamma - 1;
	unsigned int num_bin = x - pow(2, long_cod_binario); // unsigned short puedo representar hasta 65535, cambiar a int si es necesario
	unsigned int cant_bytes_binario = 0; // cantidad de bytes usados para el num binario
	unsigned int long_delta = 0;
	unsigned int cant_bytes_delta = 0;

	//std::cout << "num_gamma " << num_gamma << std::endl;

	//(*delta) = NULL;

	if(long_cod_binario % 8 == 0)
		cant_bytes_binario = long_cod_binario / 8;
	else
		cant_bytes_binario = (long_cod_binario / 8) + 1;

	CompresorGamma c_gamma;
	if(num_gamma > 0)
		long_gamma = c_gamma.comprimir(num_gamma, &gamma);
	long_delta = long_gamma + long_cod_binario;


	if(long_delta % 8 == 0) {
		cant_bytes_delta = long_delta / 8;

	}
	else {
		cant_bytes_delta = (long_delta / 8) + 1;
	}

	*delta = new unsigned char[cant_bytes_delta];
	//(*delta) = NULL;


	if(long_gamma % 8 == 0)
		cant_bytes_gamma = long_gamma / 8;
	else
		cant_bytes_gamma = (long_gamma / 8) + 1;


	// Almaceno el código gamma en el vector q representa el código delta
	for(unsigned int i = 1; i <= cant_bytes_gamma; i++) {
		(*delta)[i-1] = gamma[i-1];
		//std::cout << "Byte " << i-1 << ": " << c_u->contador_de_1((*delta)[i - 1]) << std::endl;
	}


	(*(unsigned int*)(*delta)) <<= long_cod_binario; // x <<= a igual a x = x << a

	//std::cout << num_gamma  << " en cod gamma: " << *((unsigned int*)(*delta)) << std::endl;

	for(unsigned int i = 1; i <= cant_bytes_binario; i++) {
		(*delta)[i-1] |= (num_bin >> (8 * (i-1))); // Desplazamiento de 8 bits por byte
	}

	//std::cout << num_gamma  << " en cod gamma: " << *((unsigned int*)(*delta)) << std::endl;

	//std::cout  << "Número " << x << " en código delta: " << *((unsigned int*)(*delta)) << std::endl;
	if(num_gamma > 0)
		delete[] gamma;
	return long_delta;
}

// Devuelve el numero en base 10
unsigned int CompresorDelta::descompresor(unsigned char* delta) {
	unsigned int num_delta = 0;
	unsigned int num_gamma = 0;
	unsigned int cant_bytes = 0;
	/*do {
		cant_bytes++;
	}
	while(delta[cant_bytes] == 0);*/
	if(delta[0] != 0)
		cant_bytes = 1;
	if(delta[1] != 0)
		cant_bytes = 2;
	if(delta[2] != 0)
		cant_bytes = 3;
	if(delta[3] != 0)
		cant_bytes = 4;
	//std::cout << "Cantidad de bytes ocupados por delta: " << cant_bytes << std::endl;
	//int cant_bytes = strlen((char *)delta);
	unsigned int mascara = 0;
	unsigned int num_unario = 1;
	unsigned int num_bin1 = 0; // número binario del gamma
	unsigned int long_bin1 = 0; // longitud del número binario del gamma
	unsigned int bits_leidos_bin1 = 0; // bits leidos del número binario de gamma
	unsigned int num_bin2 = 0; // número binario del delta
	unsigned int aux = *((unsigned int*)delta);
	//std::cout << "Cantidad de bytes ocupados por delta: " << cant_bytes << std::endl;
	//std::cout << "en código delta: " << aux << std::endl;


	bool dio_1 = false;
	bool fin_unario = false;
	bool fin_bin1 = false;
	//std::cout  << "Numero binario: " << aux << std::endl;
	for(int i = ((cant_bytes * 8) - 1); i >= 0; i--) {
		mascara = pow(2, i);
		if((bits_leidos_bin1 == long_bin1) && (fin_unario))
			fin_bin1 = true;
		if((long_bin1 != 0) && (bits_leidos_bin1 < long_bin1))
			bits_leidos_bin1++;
		if((mascara & aux) == 0) {
			if((dio_1) && (!fin_unario)) {
				fin_unario = true;
				long_bin1 = num_unario - 1;
			}
		}
		if((mascara & aux) == mascara) {
			if(!fin_unario) {
				dio_1 = true;
				num_unario++;
			}
			else {
				if(!fin_bin1)
					num_bin1 += pow(2, long_bin1 - bits_leidos_bin1);
				else
					num_bin2 += mascara;
			}
		}

	}

	//std::cout  << "Numero unario: " << num_unario << std::endl;
	//std::cout  << "Numero binario1: " << num_bin1 << std::endl;
	num_gamma = pow(2, num_unario - 1) + num_bin1;
	//std::cout  << "Numero gamma: " << num_gamma << std::endl;
	//std::cout  << "Numero binario2: " << num_bin2 << std::endl;
	num_delta = pow(2, num_gamma - 1) + num_bin2;
	//std::cout  << "Numero delta: " << num_delta << std::endl;
	return num_delta;
}

CompresorDelta::~CompresorDelta() {}
